package com.toilet.easyvideo.service.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.toilet.easyvideo.config.FileConfig;
import com.toilet.easyvideo.constant.StringConstant;
import com.toilet.easyvideo.domain.CheckFile;
import com.toilet.easyvideo.domain.UploadFile;
import com.toilet.easyvideo.exception.ServiceException;
import com.toilet.easyvideo.response.BasePageResponse;
import com.toilet.easyvideo.response.DownLoadFileResponse;
import com.toilet.easyvideo.response.FileResponse;
import com.toilet.easyvideo.service.CheckFileService;
import com.toilet.easyvideo.service.FileService;
import com.toilet.easyvideo.service.UploadFileService;
import com.toilet.easyvideo.util.SecurityUtils;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.io.*;
import java.util.List;

/**
 * 文件上传服务层
 */
@Slf4j
@Service
public class FileServiceImpl implements FileService {
    @Resource
    private UploadFileService uploadFileService;
    @Resource
    private CheckFileService checkFileService;
    @Resource
    private FileConfig fileConfig;

    /**
     * 根据md5查询文件是否存在
     *
     * @param md5 文件片md5
     * @return
     */
    @Override
    public Boolean selectFileByMd5(String md5) {
        return uploadFileService.selectFileByMd5(md5);
    }

    /**
     * 根据md5查询文件片是否存在
     *
     * @param md5 文件片md5
     * @return
     */
    @Override
    public List<Long> selectChunkListByMd5(String md5) {
        return checkFileService.selectChunkListByMd5(md5);
    }

    /**
     * 保存文件片
     *
     * @param fileContent    文件片
     * @param md5            文件片md5
     * @param chunkNumber    文件片序号
     * @param chunkSize      文件大小
     * @param resultFileName 文件名称
     * @return 保存结果
     */
    @Override
    public Integer saveChunk(byte[] fileContent, String md5,
                             Long chunkNumber, Long chunkSize,
                             String resultFileName, Long userId) {
        try (RandomAccessFile accessFile = new RandomAccessFile(resultFileName, "rw")) {

            long offset = chunkSize * (chunkNumber - 1);
            log.info("当前偏移量为：{}", offset);
            // 定位到该分片的偏移量
            accessFile.seek(offset);
            // 写入
            accessFile.write(fileContent);
            CheckFile checkFile = new CheckFile();
            checkFile.setCheckMd5(md5);
            checkFile.setCheckIndex(chunkNumber);
            checkFile.setUserId(userId);
            // 保存数据
            return checkFileService.save(checkFile) ? 1 : 0;
        } catch (Exception e) {
            log.error("保存文件片失败", e);
            return 0;
        }
    }

    /**
     * 保存文件
     *
     * @param fileName 文件名称
     * @param md5      文件md5值
     * @param fileSize 文件大小
     * @return 保存结果
     */
    @Override
    public Integer addFile(String fileName, String md5, Long fileSize, Long userId) {
        UploadFile uploadFile = new UploadFile();
        uploadFile.setUserId(SecurityUtils.getUserId());
        uploadFile.setFileName(fileName);
        uploadFile.setFileSize(fileSize);
        uploadFile.setFileMd5(md5);
        uploadFile.setUserId(userId);
        String[] splits = fileName.split("\\.");
        String type = splits[splits.length - 1];
        uploadFile.setFileType(type);
        boolean save = uploadFileService.save(uploadFile);
        return save ? 1 : 0;
    }

    /**
     * 删除文件片
     *
     * @param md5 文件片md5
     */
    @Override
    public void deleteChunkByMd5(String md5, Long userId) {
        QueryWrapper<CheckFile> wrapper = new QueryWrapper<>();
        wrapper.eq("check_md5", md5)
                .eq("user_id", userId);
        checkFileService.remove(wrapper);
    }

    /**
     * 文件列表分页查询
     *
     * @param pageSize    页码大小
     * @param currentPage 当前页
     * @return 分页数据
     */
    @Override
    public BasePageResponse<FileResponse> fileList(Long pageSize, Long currentPage, Long userId) {
        Page<UploadFile> filePage = uploadFileService.lambdaQuery()
                .eq(UploadFile::getUserId, userId)
                .page(new Page<>(pageSize, currentPage));
        // 封装分页数据
        BasePageResponse<FileResponse> pageResponse = new BasePageResponse<>();
        pageResponse.setTotal(filePage.getTotal());
        pageResponse.setCurrentPage(filePage.getCurrent());
        pageResponse.setPageSize(filePage.getSize());
        pageResponse.setData(filePage.getRecords().stream().map(file -> {
            FileResponse fileResponse = new FileResponse();
            fileResponse.setFileName(file.getFileName());
            fileResponse.setFileSize(file.getFileSize());
            fileResponse.setFileMd5(file.getFileMd5());
            fileResponse.setId(file.getId());
            return fileResponse;
        }).toList());

        return pageResponse;
    }

    @Override
    public DownLoadFileResponse selectFileById(Long fileId, Long currentSize, Long chunkSize) {
        UploadFile uploadFile = uploadFileService.getById(fileId);
        if (uploadFile == null) {
            throw new ServiceException("文件不存在");
        }
        DownLoadFileResponse fileResponse = new DownLoadFileResponse();

        fileResponse.setFileName(uploadFile.getFileMd5() + "." + uploadFile.getFileType());

        try (RandomAccessFile randomAccessFile = new RandomAccessFile(fileConfig.getUploadPath()
                + uploadFile.getFileMd5() + "." + uploadFile.getFileType(), "r")) {
            // 定位到该分片的偏移量
            randomAccessFile.seek(currentSize);
            //读取
            byte[] buffer = new byte[chunkSize.intValue()];
            randomAccessFile.read(buffer);
            fileResponse.setChunk(buffer);
            return fileResponse;
        } catch (IOException e) {
            throw new ServiceException("文件读取异常");
        }
    }

    @Override
    public Boolean deleteFileById(Long fileId, Long userId) {
        UploadFile uploadFile = uploadFileService.lambdaQuery()
                .eq(UploadFile::getId, fileId)
                .eq(UploadFile::getUserId, userId)
                .one();
        if (ObjectUtil.isEmpty(uploadFile)) {
            return Boolean.FALSE;
        }
        boolean remove = uploadFileService.removeById(fileId);
        if (remove) {
            File file = new File(fileConfig.getUploadPath() + uploadFile.getFileMd5()
                    + StringConstant.DOT + uploadFile.getFileType());
            if (file.exists()) {
                boolean delete = file.delete();
                log.info("用户{}删除文件,删除结果：", delete);
                return delete;
            }
        }
        return Boolean.FALSE;
    }
}
